package com.cloud.lan.config.redis;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.RedisSerializer;

import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;

/**
 * Redis客户端
 * @author ErKang
 * @date Created in 2021/12/7 19:16
 * @title RedisClient.java
 * Department: Product development
 */
public class RedisClient {

    public static final String LEFIT_LIVE_DISTRIBUTED_LOCK = "LITTA_DL_LOCK#";
    private static final Logger logger = LoggerFactory.getLogger(RedisClient.class);
    public static RedisClient DEFAULT;
    private final String lockPrefix;
    private final ThreadLocal<RedisLock> threadLocalLock = new ThreadLocal<>();
    private StringRedisTemplate redisTemplate;

    public RedisClient(StringRedisTemplate redisTemplate) {
        this(redisTemplate, LEFIT_LIVE_DISTRIBUTED_LOCK);
    }

    public RedisClient(StringRedisTemplate redisTemplate, String lockPrefix) {
        if (Objects.isNull(redisTemplate) || StrUtil.isBlank(lockPrefix)) {
            throw new NullPointerException();
        }
        this.redisTemplate = redisTemplate;
        this.lockPrefix = lockPrefix;
    }

    public static RedisClient getDefault() {
        return DEFAULT;
    }

    public static void setDefault(RedisClient redisClient) {
        RedisClient.DEFAULT = redisClient;
    }

    public StringRedisTemplate getRedisTemplate() {
        return redisTemplate;
    }

    /**
     * 获取主库的字符串序列化对象
     *
     * @return
     */
    protected RedisSerializer<String> getRedisMasterSerializer() {
        RedisSerializer<String> redisSerializer = getRedisTemplate().getStringSerializer();
        return redisSerializer;
    }

    /**
     * 获取从库的字符串序列化对象
     *
     * @return
     */
    protected RedisSerializer<String> getRedisSlaveSerializer() {
        RedisSerializer<String> redisSerializer = getRedisTemplate().getStringSerializer();
        return redisSerializer;
    }

    /**
     * 字符串类型:通过key值获取对应的value对象
     *
     * @param key
     * @return
     */
    public String get(final String key) {
        return redisTemplate.execute(
                (RedisCallback<String>)
                        redisConnection -> {
                            RedisSerializer<String> serializer = getRedisSlaveSerializer();
                            byte keys[] = serializer.serialize(key);
                            byte values[] = redisConnection.get(keys);
                            if (values == null) {
                                return null;
                            }
                            String value = serializer.deserialize(values);
                            return value;
                        });
    }

    /**
     * 字符串类型:存入key-value对象，如果key存在，那么默认更新value
     *
     * @param key
     * @param value
     * @return
     */
    public Boolean set(final String key, final String value) {
        return redisTemplate.execute(
                (RedisCallback<Boolean>)
                        redisConnection -> {
                            RedisSerializer<String> serializer = getRedisMasterSerializer();
                            byte keys[] = serializer.serialize(key);
                            byte values[] = serializer.serialize(value);
                            redisConnection.set(keys, values);
                            return true;
                        });
    }

    /**
     * 字符串类型:通过key删除对应的key和value
     *
     * @param key
     * @return
     */
    public Long delete(final String key) {
        return redisTemplate.execute(
                (RedisCallback<Long>)
                        redisConnection -> {
                            RedisSerializer<String> redisSerializer = getRedisMasterSerializer();
                            byte[] keys = redisSerializer.serialize(key);
                            return redisConnection.del(keys);
                        });
    }

    /**
     * 字符串类型:通过key判断对象是否存在
     *
     * @param key
     * @return
     */
    public Boolean exists(final String key) {
        return redisTemplate.execute(
                (RedisCallback<Boolean>)
                        redisConnection -> {
                            byte[] keys = getRedisSlaveSerializer().serialize(key);
                            return redisConnection.exists(keys);
                        });
    }

    /**
     * 字符串类型:设置key对应的超时时间
     *
     * @param key
     * @param expireTime
     * @return
     */
    public Boolean expire(final String key, final long expireTime) {
        return redisTemplate.execute(
                (RedisCallback<Boolean>)
                        redisConnection -> {
                            byte[] keys = getRedisMasterSerializer().serialize(key);
                            return redisConnection.expire(keys, expireTime);
                        });
    }

    /**
     * 字符串类型:根据key设置value值,如果key中的value存在,那么返回false
     *
     * @param key
     * @param value
     * @return
     */
    public Boolean setnx(final String key, final String value) {
        return redisTemplate.execute(
                (RedisCallback<Boolean>)
                        redisConnection -> {
                            byte[] keys = getRedisMasterSerializer().serialize(key);
                            byte[] values = getRedisMasterSerializer().serialize(value);
                            return redisConnection.setNX(keys, values);
                        });
    }

    public String getSet(final String key, final String value) {
        return redisTemplate.execute(
                (RedisCallback<String>)
                        redisConnection -> {
                            byte[] keys = getRedisMasterSerializer().serialize(key);
                            byte[] values = getRedisMasterSerializer().serialize(value);
                            return getRedisMasterSerializer()
                                    .deserialize(redisConnection.getSet(keys, values));
                        });
    }

    /**
     * 字符串类型:设置key和value的超时时间(设置成String返回类型,不然要设置成Void)
     *
     * @param key
     * @param timeout
     * @param value
     * @return
     */
    public Boolean setex(final String key, final long timeout, final String value) {
        return redisTemplate.execute(
                (RedisCallback<Boolean>)
                        redisConnection -> {
                            byte[] keys = getRedisMasterSerializer().serialize(key);
                            byte[] values = getRedisMasterSerializer().serialize(value);
                            return redisConnection.setEx(keys, timeout, values);
                        });
    }

    /**
     * list列表类型:先进后出栈形式,单个值插入
     *
     * @param key
     * @param value
     * @return
     */
    public Long lpush(final String key, final String value) {
        return redisTemplate.execute(
                (RedisCallback<Long>)
                        redisConnection -> {
                            byte keys[] = getRedisMasterSerializer().serialize(key);
                            byte values[] = getRedisMasterSerializer().serialize(value);
                            return redisConnection.lPush(keys, values);
                        });
    }

    /**
     * list列表类型：移除数据
     *
     * @param key
     * @return
     */
    public String rpop(final String key) {
        return redisTemplate.execute(
                (RedisCallback<String>)
                        redisConnection -> {
                            byte keys[] = getRedisMasterSerializer().serialize(key);
                            byte result[] = redisConnection.rPop(keys);
                            return result == null ? null : new String(result);
                        });
    }

    /**
     * list列表类型:先进先出队列形式,单个值插入
     *
     * @param key
     * @param value
     * @return
     */
    public Long rpush(final String key, final String value) {
        return redisTemplate.execute(
                (RedisCallback<Long>)
                        redisConnection -> {
                            byte keys[] = getRedisMasterSerializer().serialize(key);
                            byte values[] = getRedisMasterSerializer().serialize(value);
                            return redisConnection.rPush(keys, values);
                        });
    }

    /**
     * list列表类型: 返回存储在key的列表里指定范围内的元素。Start和end偏移量都是基于0的下标， 即list的第一个元素下标是0(list的开头)，第二个元素是下标1，以此类推。
     * 偏移量也可以是负数，表示偏移量是从list尾部开始计数。例如，-1表示列表的最后一个元素，-2是倒数第二个，以此类推。
     *
     * @param key
     * @param start
     * @param end
     * @return
     */
    public List<byte[]> lrange(final String key, final Long start, final Long end) {
        return redisTemplate.execute(
                (RedisCallback<List<byte[]>>)
                        redisConnection -> {
                            byte keys[] = getRedisMasterSerializer().serialize(key);
                            return redisConnection.lRange(keys, start, end);
                        });
    }

    /**
     * list列表类型:返回名称为key的list中index位置的元素
     *
     * @param key
     * @param offset
     * @return
     */
    public byte[] lindex(final String key, final Long offset) {
        return redisTemplate.execute(
                (RedisCallback<byte[]>)
                        redisConnection -> {
                            byte keys[] = getRedisSlaveSerializer().serialize(key);
                            return redisConnection.lIndex(keys, offset);
                        });
    }

    /**
     * list列表类型:返回list中的元素个数
     *
     * @param key
     * @return
     */
    public Long llen(final String key) {
        return redisTemplate.execute(
                (RedisCallback<Long>)
                        redisConnection -> {
                            byte keys[] = getRedisSlaveSerializer().serialize(key);
                            return redisConnection.lLen(keys);
                        });
    }

    /**
     * set无序集合类型: 添加一个或多个指定的member元素到集合的key中
     *
     * @param key
     * @param bytes
     * @return
     */
    public Long sadd(final String key, final byte[]... bytes) {
        return redisTemplate.execute(
                (RedisCallback<Long>)
                        redisConnection -> {
                            byte keys[] = getRedisMasterSerializer().serialize(key);
                            return redisConnection.sAdd(keys, bytes);
                        });
    }

    /**
     * set无序集合类型: 返回key集合所有的元素
     *
     * @param key
     * @return
     */
    public Set<byte[]> smembers(final String key) {
        return redisTemplate.execute(
                (RedisCallback<Set<byte[]>>)
                        redisConnection -> {
                            byte keys[] = getRedisSlaveSerializer().serialize(key);
                            return redisConnection.sMembers(keys);
                        });
    }

    /**
     * set无序集合类型:删除key中的指定元素
     *
     * @param key
     * @param bytes
     * @return
     */
    public Long srem(final String key, final byte[]... bytes) {
        return redisTemplate.execute(
                (RedisCallback<Long>)
                        redisConnection -> {
                            byte keys[] = getRedisMasterSerializer().serialize(key);
                            return redisConnection.sRem(keys, bytes);
                        });
    }

    /**
     * set无序集合类型: 返回指定的所有集合的成员的交集。如果key不存在则被认为是一个空的集合，当给定的集合为空的时候，结果也为空(一个集合为空，结果一直为空)
     *
     * @param bytes
     * @return
     */
    public Set<byte[]> sinter(final byte[]... bytes) {
        return redisTemplate.execute(
                (RedisCallback<Set<byte[]>>) redisConnection -> redisConnection.sInter(bytes));
    }

    /**
     * set无序集合类型: 把两个集合的交集结果集结果保存到一个新的集合中。如果这个新的集合是已经存在的，那么这个新的集合则会被重写。 返回值为结果集中成员的个数。
     *
     * @param key
     * @param bytes
     * @return
     */
    public Long sinterstore(final String key, final byte[]... bytes) {
        return redisTemplate.execute(
                (RedisCallback<Long>)
                        redisConnection -> {
                            byte keys[] = getRedisMasterSerializer().serialize(key);
                            return redisConnection.sInterStore(keys, bytes);
                        });
    }

    /**
     * set无序集合类型: 返回给定的多个集合的并集中的所有成员，不存在的key可以认为是空的集合。 返回值为并集的成员列表。
     *
     * @param bytes
     * @return
     */
    public Set<byte[]> sunion(final byte[]... bytes) {
        return redisTemplate.execute(
                (RedisCallback<Set<byte[]>>) redisConnection -> redisConnection.sUnion(bytes));
    }

    /**
     * set无序集合类型: 将集合结果并集存储在新的集合中，如果新的集合已经存在，那么会覆盖这个新的集合。 返回值为结果集中元素的个数。
     *
     * @param key
     * @param bytes
     * @return
     */
    public Long sunionstore(final String key, final byte[]... bytes) {
        return redisTemplate.execute(
                (RedisCallback<Long>)
                        redisConnection -> {
                            byte keys[] = getRedisMasterSerializer().serialize(key);
                            return redisConnection.sUnionStore(keys, bytes);
                        });
    }

    /**
     * set无序集合类型:
     *
     * @param key1 被移除的集合
     * @param key2 接收移除元素的集合
     * @param field 要移除的元素
     * @return
     */
    public Boolean smove(final String key1, final String key2, final String field) {
        return redisTemplate.execute(
                (RedisCallback<Boolean>)
                        redisConnection ->
                                redisConnection.sMove(
                                        getRedisMasterSerializer().serialize(key1),
                                        getRedisMasterSerializer().serialize(key2),
                                        getRedisMasterSerializer().serialize(field)));
    }

    /**
     * set无序集合类型:返回集合中的元素个数
     *
     * @param key
     * @return
     */
    public Long scard(final String key) {
        return redisTemplate.execute(
                (RedisCallback<Long>)
                        redisConnection -> {
                            byte[] keys = getRedisSlaveSerializer().serialize(key);
                            return redisConnection.sCard(keys);
                        });
    }

    /**
     * set无序集合类型:判断bytes2是否是bytes1中的元素
     *
     * @param key
     * @param value
     * @return
     */
    public Boolean sismember(final String key, final String value) {
        return redisTemplate.execute(
                (RedisCallback<Boolean>)
                        redisConnection ->
                                redisConnection.sIsMember(
                                        getRedisSlaveSerializer().serialize(key),
                                        getRedisSlaveSerializer().serialize(value)));
    }

    /**
     * zset有序集合类型:
     *
     * @param key
     * @param expireat 分数值是一个双精度的浮点型数字字符串
     * @param bytes2 value
     * @return
     */
    public Boolean zaddLimit(
            final String key, final Long expireat, final byte[] bytes2, int limitNum) {
        return redisTemplate.execute(
                (RedisCallback<Boolean>)
                        redisConnection -> {
                            byte[] sKey = getRedisMasterSerializer().serialize(key);
                            Boolean bo = redisConnection.zAdd(sKey, expireat, bytes2);
                            Long count = redisConnection.zCard(sKey);
                            if (count > limitNum) {
                                redisConnection.zRemRange(sKey, 0, count - limitNum - 1);
                            }
                            redisConnection.pExpireAt(sKey, expireat);
                            return bo;
                        });
    }

    /**
     * zset有序集合类型:根据集合中指定的index返回成员列表
     *
     * @param key
     * @param start
     * @param end
     * @return
     */
    public Set<byte[]> zrange(final String key, final Long start, final Long end) {
        return redisTemplate.execute(
                (RedisCallback<Set<byte[]>>)
                        redisConnection ->
                                redisConnection.zRange(
                                        getRedisSlaveSerializer().serialize(key), start, end));
    }

    /**
     * zset有序集合类型:根据集合中指定的index返回成员列表
     *
     * @param key
     * @param rmStart
     * @param rmEnd
     * @param getStart
     * @param getEnd
     * @return
     */
    public Set<byte[]> zrangeandrem(
            final String key,
            final Double rmStart,
            final Double rmEnd,
            final Long getStart,
            final Long getEnd) {
        return redisTemplate.execute(
                (RedisCallback<Set<byte[]>>)
                        redisConnection -> {
                            byte[] keys = getRedisMasterSerializer().serialize(key);
                            redisConnection.zRemRangeByScore(keys, rmStart, rmEnd);
                            Set<byte[]> set = redisConnection.zRange(keys, getStart, getEnd);
                            byte[][] bytes = set.toArray(new byte[set.size()][]);
                            if (bytes.length != 0) {
                                redisConnection.zRem(keys, bytes);
                            }
                            return set;
                        });
    }

    /**
     * zset有序集合类型: 从排序的集合中删除一个或多个成员 返回值为从有序集合中删除的成员个数，不包括不存在的成员
     *
     * @param key
     * @param bytes
     * @return
     */
    public Long zrem(final String key, final byte[]... bytes) {
        return redisTemplate.execute(
                (RedisCallback<Long>)
                        redisConnection -> {
                            byte[] keys = getRedisSlaveSerializer().serialize(key);
                            return redisConnection.zRem(keys, bytes);
                        });
    }

    /**
     * zset有序集合类型:为有序集key的成员member的offset值加上增量increment
     *
     * @param key key
     * @param offset 增量increment
     * @param field 集合成员
     * @return member成员的新offset值
     */
    public Double zincrby(final String key, final Double offset, final String field) {
        return redisTemplate.execute(
                (RedisCallback<Double>)
                        redisConnection ->
                                redisConnection.zIncrBy(
                                        getRedisMasterSerializer().serialize(key),
                                        offset,
                                        getRedisMasterSerializer().serialize(field)));
    }

    /**
     * zset有序集合类型:找到指定区间范围的数据进行返回
     *
     * @param key
     * @param start
     * @param end
     * @return
     */
    public Set<byte[]> zrangebyscore(final String key, final Double start, final Double end) {
        return redisTemplate.execute(
                (RedisCallback<Set<byte[]>>)
                        redisConnection ->
                                redisConnection.zRangeByScore(
                                        getRedisSlaveSerializer().serialize(key), start, end));
    }

    /**
     * zset有序集合类型:移除有序集key中，指定排名(rank)区间内的所有成员。
     *
     * @param key
     * @param start
     * @param end
     * @return
     */
    public Long zremrangebyrank(final String key, final Double start, final Double end) {
        return redisTemplate.execute(
                (RedisCallback<Long>)
                        redisConnection -> {
                            byte[] keys = getRedisMasterSerializer().serialize(key);
                            return redisConnection.zRemRangeByScore(keys, start, end);
                        });
    }

    /**
     * zset有序集合类型:移除有序集key中，所有 score 值介于 min 和 max 之间(包括等于 min 或 max )的成员
     *
     * @param key
     * @param start
     * @param end
     * @return
     */
    public Long zremrangescore(final String key, final Double start, final Double end) {
        return redisTemplate.execute(
                (RedisCallback<Long>)
                        redisConnection -> {
                            byte[] keys = getRedisMasterSerializer().serialize(key);
                            return redisConnection.zRemRangeByScore(keys, start, end);
                        });
    }

    /**
     * zset有序集合类型:返回有序集key中，score值在min和max之间(默认包括score值等于min或max)的成员。
     *
     * @param key
     * @param start
     * @param end
     * @return
     */
    public Long zcount(final String key, final Double start, final Double end) {
        return redisTemplate.execute(
                (RedisCallback<Long>)
                        redisConnection -> {
                            byte[] keys = getRedisSlaveSerializer().serialize(key);
                            return redisConnection.zCount(keys, start, end);
                        });
    }

    /**
     * hash类型: 设置 key 指定的哈希集中指定字段的值 如果 key 指定的哈希集不存在，会创建一个新的哈希集并与 key 关联 如果字段在哈希集中存在，它将被重写
     *
     * @param key
     * @param field
     * @param value
     * @return
     */
    public Boolean hset(final String key, final String field, final String value) {
        return redisTemplate.execute(
                (RedisCallback<Boolean>)
                        redisConnection ->
                                redisConnection.hSet(
                                        getRedisMasterSerializer().serialize(key),
                                        getRedisMasterSerializer().serialize(field),
                                        getRedisMasterSerializer().serialize(value)));
    }

    /**
     * hash类型:返回 key 指定的哈希集中该字段所关联的值
     *
     * @param key
     * @param field
     * @return
     */
    public byte[] hget(final String key, final String field) {
        return redisTemplate.execute(
                (RedisCallback<byte[]>)
                        redisConnection ->
                                redisConnection.hGet(
                                        getRedisSlaveSerializer().serialize(key),
                                        getRedisSlaveSerializer().serialize(field)));
    }

    /**
     * hash类型:返回 key 指定的哈希集中所有字段的名字
     *
     * @param key
     * @return
     */
    public Set<byte[]> hkeys(final String key) {
        return redisTemplate.execute(
                (RedisCallback<Set<byte[]>>)
                        redisConnection ->
                                redisConnection.hKeys(getRedisSlaveSerializer().serialize(key)));
    }

    /**
     * hash类型:从 key 指定的哈希集中移除指定的域。
     *
     * @param key
     * @param fields
     * @return
     */
    public Long hdel(final String key, final byte[]... fields) {
        return redisTemplate.execute(
                (RedisCallback<Long>)
                        redisConnection ->
                                redisConnection.hDel(
                                        getRedisMasterSerializer().serialize(key), fields));
    }

    /**
     * hash类型:返回 key 指定的哈希集包含的字段的数量
     *
     * @param key
     * @return
     */
    public Long hlen(final String key) {
        return redisTemplate.execute(
                (RedisCallback<Long>)
                        redisConnection ->
                                redisConnection.hLen(getRedisSlaveSerializer().serialize(key)));
    }

    /**
     * hash类型:可同时对key设置多个值，hset只能一次设置一个
     *
     * @param key
     * @param map
     * @return
     */
    public Void hmset(final String key, final Map<byte[], byte[]> map) {
        return redisTemplate.execute(
                (RedisCallback<Void>)
                        redisConnection -> {
                            redisConnection.hMSet(getRedisMasterSerializer().serialize(key), map);
                            return null;
                        });
    }

    /**
     * hash类型:返回 key 指定的哈希集中指定多个字段的值。
     *
     * @param key
     * @param fields
     * @return
     */
    public List<byte[]> hmget(final String key, final byte[]... fields) {
        return redisTemplate.execute(
                (RedisCallback<List<byte[]>>)
                        redisConnection ->
                                redisConnection.hMGet(
                                        getRedisSlaveSerializer().serialize(key), fields));
    }

    /**
     * hash类型: 增加 key 指定的哈希集中指定字段的数值。 如果 key 不存在，会创建一个新的哈希集并与 key 关联。如果字段不存在，则字段的值在该操作执行前被设置为 0
     * HINCRBY 支持的值的范围限定在 64位 有符号整数
     *
     * @param key
     * @param field
     * @param val
     * @return
     */
    public Double hincrby(final String key, final String field, final Double val) {
        return redisTemplate.execute(
                (RedisCallback<Double>)
                        redisConnection ->
                                redisConnection.hIncrBy(
                                        getRedisMasterSerializer().serialize(key),
                                        getRedisMasterSerializer().serialize(field),
                                        val));
    }

    /**
     * hash类型:返回hash里面key是否存在的标志
     *
     * @param key
     * @param field
     * @return
     */
    public Boolean hexists(final String key, final String field) {
        return redisTemplate.execute(
                (RedisCallback<Boolean>)
                        redisConnection ->
                                redisConnection.hExists(
                                        getRedisSlaveSerializer().serialize(key),
                                        getRedisSlaveSerializer().serialize(field)));
    }

    /**
     * hash类型:返回 key 指定的哈希集中所有字段的值。
     *
     * @param key
     * @return
     */
    public List<byte[]> hvals(final String key) {
        return redisTemplate.execute(
                (RedisCallback<List<byte[]>>)
                        redisConnection ->
                                redisConnection.hVals(getRedisSlaveSerializer().serialize(key)));
    }

    /**
     * hash类型: 只在 key 指定的哈希集中不存在指定的字段时，设置字段的值。 如果 key 指定的哈希集不存在，会创建一个新的哈希集并与 key 关联。 如果字段已存在，该操作无效果。
     *
     * @param key
     * @param field
     * @param value
     * @return
     */
    public Boolean hsetnx(final String key, final String field, final String value) {
        return redisTemplate.execute(
                (RedisCallback<Boolean>)
                        redisConnection ->
                                redisConnection.hSetNX(
                                        getRedisMasterSerializer().serialize(key),
                                        getRedisMasterSerializer().serialize(field),
                                        getRedisMasterSerializer().serialize(value)));
    }

    /**
     * hash类型: 返回 key 指定的哈希集中所有的字段和值。返回值中，每个字段名的下一个是它的值，所以返回值的长度是哈希集大小的两倍
     *
     * @param key
     * @return
     */
    public Map<byte[], byte[]> hgetall(final String key) {
        return redisTemplate.execute(
                (RedisCallback<Map<byte[], byte[]>>)
                        redisConnection ->
                                redisConnection.hGetAll(getRedisSlaveSerializer().serialize(key)));
    }

    /**
     * 分布式锁
     *
     * @param key
     * @return
     */
    public boolean lock(String key) {
        try {
            RedisLock lock = new RedisLock(key);
            threadLocalLock.set(lock);
            return lock.lock();
        } catch (Exception e) {
            logger.error("lock error", e);
        }
        return false;
    }

    /**
     * 解锁（不做是否已解锁判断）
     *
     * @param key
     * @return
     */
    public boolean unLock(String key) {
        try {
            RedisLock lock = threadLocalLock.get();
            if (null != lock) {
                logger.debug("main unLock");
                lock.unlock();
            }
            return true;
        } catch (Exception e) {
            logger.error("unLock error", e);
        }
        return false;
    }

    class RedisLock {
        /** 重试时间间隔 */
        private final int retryMsecs = 100;
        private final String lockKey;
        /** 锁超时时间，防止线程在入锁以后，无限的执行等待 */
        private int expireMsecs = 60 * 1000;
        /** 锁等待时间，防止线程饥饿 */
        private int timeoutMsecs = 10 * 1000;
        private volatile boolean locked = false;

        /**
         * Detailed constructor with default acquire timeout 10000 msecs and lock expiration of
         * 60000 msecs.
         *
         * @param lockKey lock key (ex. account:1, ...)
         */
        public RedisLock(String lockKey) {
            this.lockKey = lockPrefix + lockKey;
        }

        /** Detailed constructor with default lock expiration of 60000 msecs. */
        public RedisLock(String lockKey, int timeoutMsecs) {
            this.lockKey = lockPrefix + lockKey;
            this.timeoutMsecs = timeoutMsecs;
        }

        /** Detailed constructor. */
        public RedisLock(String lockKey, int timeoutMsecs, int expireMsecs) {
            this.lockKey = lockPrefix + lockKey;
            this.timeoutMsecs = timeoutMsecs;
            this.expireMsecs = expireMsecs;
        }

        /**
         * 获得 lock. 实现思路: 主要是使用了redis 的setnx命令,缓存了锁. reids缓存的key是锁的key,所有的共享,
         * value是锁的到期时间(注意:这里把过期时间放在value了,没有时间上设置其超时时间) 执行过程:
         * 1.通过setnx尝试设置某个key的值,成功(当前没有这个锁)则返回,成功获得锁 2.锁已经存在则获取锁的到期时间,和当前时间比较,超时的话,则设置新的值
         *
         * @return true if lock is acquired, false acquire timeout
         * @throws InterruptedException in case of thread interruption
         */
        public boolean lock() throws InterruptedException {
            int timeout = timeoutMsecs;
            while (timeout >= 0) {
                long expires = System.currentTimeMillis() + expireMsecs + 1;
                String expiresStr = String.valueOf(expires); // 锁到期时间
                logger.debug("expiresStr:{}", expiresStr);
                if (setnx(lockKey, expiresStr)) {
                    logger.debug("is locked");
                    // lock acquired
                    locked = true;
                    return true;
                }

                logger.debug("if over time");
                String currentValueStr = get(lockKey); // redis里的时间
                if (currentValueStr != null
                        && Long.parseLong(currentValueStr) < System.currentTimeMillis()) {
                    // 判断是否为空，不为空的情况下，如果被其他线程设置了值，则第二个条件判断是过不去的
                    // lock is expired

                    String oldValueStr = getSet(lockKey, expiresStr);
                    // 获取上一个锁到期时间，并设置现在的锁到期时间，
                    // 只有一个线程才能获取上一个线上的设置时间，因为jedis.getSet是同步的
                    if (oldValueStr != null && oldValueStr.equals(currentValueStr)) {
                        // 防止误删（覆盖，因为key是相同的）了他人的锁——这里达不到效果，这里值会被覆盖，但是因为什么相差了很少的时间，所以可以接受

                        // [分布式的情况下]:如过这个时候，多个线程恰好都到了这里，但是只有一个线程的设置值和当前值相同，他才有权利获取锁
                        // lock acquired
                        logger.debug("over time locked");
                        locked = true;
                        return true;
                    }
                }

                long retryWait = RandomUtil.randomLong(30, retryMsecs);
                timeout -= retryWait;
                logger.debug("retry:{}", timeout);
                /*
                   延迟100 毫秒,  这里使用随机时间可能会好一点,可以防止饥饿进程的出现,即,当同时到达多个进程,
                   只会有一个进程获得锁,其他的都用同样的频率进行尝试,后面有来了一些进行,也以同样的频率申请锁,这将可能导致前面来的锁得不到满足.
                   使用随机的等待时间可以一定程度上保证公平性
                */
                Thread.sleep(retryWait);
            }
            return false;
        }

        /** Acqurired lock release. */
        public synchronized void unlock() {
            if (locked) {
                logger.debug("unlock");
                delete(lockKey);
                locked = false;
            }
        }
    }
}
